/*******************************************************************************
 * Copyright (c) 2009 Markus Knittig and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Markus Knittig - initial API and implementation
 *******************************************************************************/
package com.googlecode.mylyn.ui;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.URLHyperlink;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.mylyn.tasks.core.IRepositoryQuery;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskComment;
import org.eclipse.mylyn.tasks.core.ITaskMapping;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.ui.AbstractRepositoryConnectorUi;
import org.eclipse.mylyn.tasks.ui.TaskHyperlink;
import org.eclipse.mylyn.tasks.ui.wizards.ITaskRepositoryPage;
import org.eclipse.mylyn.tasks.ui.wizards.ITaskSearchPage;
import org.eclipse.mylyn.tasks.ui.wizards.NewTaskWizard;
import org.eclipse.mylyn.tasks.ui.wizards.RepositoryQueryWizard;

import com.googlecode.mylyn.core.GoogleCodeCorePlugin;
import com.googlecode.mylyn.core.util.QueryUtils;
import com.googlecode.mylyn.ui.wizard.GoogleCodeDefinitionQueryPage;
import com.googlecode.mylyn.ui.wizard.GoogleCodeQuerySelectionPage;
import com.googlecode.mylyn.ui.wizard.GoogleCodeRepositorySettingsPage;

public class GoogleCodeConnectorUi extends AbstractRepositoryConnectorUi {

    private static final String ISSUE = "Issue";

    private static final String REVISION = "r";

    private static final String SPACE = " ?#? ?";

    private static final String INTEGER = "\\d+";

    /**
     * Regular expression to extract issue ids.
     * <ol>
     * <li>Issue (case insensitive)</li>
     * <li>optional:
     *   <ol>
     *     <li>space</li>
     *     <li>#</li>
     *   </ol>
     * </li>
     * <li>number, once or many times</li>
     * </ol>
     */
    private static final Pattern ISSUE_PATTERN = Pattern.compile("(" + ISSUE + ")(" + SPACE + ")(" + INTEGER + ")",
        Pattern.CASE_INSENSITIVE);

    /**
     * Regular expression to extract revision ids.
     * <ol>
     * <li>r (case insensitive)</li>
     * <li>optional:
     *   <ol>
     *     <li>space</li>
     *     <li>#</li>
     *   </ol>
     * </li>
     * <li>number, once or many times</li>
     * </ol>
     */
    private static final Pattern REVISION_PATTERN = Pattern.compile("(" + REVISION + ")(" + INTEGER + ")",
            Pattern.CASE_INSENSITIVE);

    /**
     * Index of the group in which the issue id is.
     */
    private static final int ISSUE_ID_GROUP = 3;

    /**
     * Index of the group in which the revision id is.
     */
    private static final int REVISION_ID_GROUP = 2;

    @Override
    public String getConnectorKind() {
        return GoogleCodeCorePlugin.CONNECTOR_KIND;
    }

    @Override
    public IWizard getNewTaskWizard(TaskRepository taskRepository, ITaskMapping selection) {
        return new NewTaskWizard(taskRepository, selection);
    }

    @Override
    public IWizard getQueryWizard(TaskRepository taskRepository, IRepositoryQuery queryToEdit) {
        RepositoryQueryWizard wizard = new RepositoryQueryWizard(taskRepository);
        if (queryToEdit == null || QueryUtils.isCanQuery(queryToEdit)) {
            //add the query selection page only for new queries or predefined ones,
            //not for existing custom ones
            wizard.addPage(new GoogleCodeQuerySelectionPage(taskRepository, queryToEdit));
        }
        wizard.addPage(new GoogleCodeDefinitionQueryPage(taskRepository, queryToEdit));
        return wizard;
    }

    @Override
    public ITaskSearchPage getSearchPage(TaskRepository repository, IStructuredSelection selection) {
        return new GoogleCodeDefinitionQueryPage(repository, null);
    }

    @Override
    public String getTaskKindLabel(ITask task) {
        //This prefix is used in the task editor title bar "Issue 23"
        //could be changed to ITask#getTaskKind()
        return "Issue";
    }

    @Override
    public ImageDescriptor getTaskKindOverlay(ITask task) {
        String taskType = task.getTaskKind();
        if (taskType != null) {
            if ("Enhancement".equals(taskType)) { //$NON-NLS-1$
                return GoogleCodeImages.OVERLAY_ENHANCEMENT;
            } else {
                return null;
            }
        }
        return super.getTaskKindOverlay(task);
    }

    @Override
    public IHyperlink[] findHyperlinks(TaskRepository repository, String text, int index, int textOffset) {
        List<IHyperlink> links = new ArrayList<IHyperlink>();

        Matcher matcher = ISSUE_PATTERN.matcher(text);
        while (matcher.find()) {
            if (index == -1 || (index >= matcher.start() && index <= matcher.end())) {
                IHyperlink link = extractTaskHyperlink(repository, textOffset, matcher);
                links.add(link);
            }
        }

        matcher = REVISION_PATTERN.matcher(text);
        while (matcher.find()) {
            if (index == -1 || (index >= matcher.start() && index <= matcher.end())) {
                IHyperlink link = extractRevisionHyperlink(repository, textOffset, matcher);
                links.add(link);
            }
        }

        return links.toArray(new IHyperlink[links.size()]);
    }

    private static IHyperlink extractRevisionHyperlink(TaskRepository repository, int regionOffset, Matcher matcher) {
        int start = matcher.start();
        int end = matcher.end();
        start += regionOffset;
        end += regionOffset;

        String revisionId = matcher.group(REVISION_ID_GROUP).trim();

        IRegion region = new Region(start, end - start);

        return new URLHyperlink(region, repository.getRepositoryUrl() + "/source/detail?r=" + revisionId);
    }

    private static IHyperlink extractTaskHyperlink(TaskRepository repository, int regionOffset,
        Matcher matcher) {

      int start = matcher.start();
      int end = matcher.end();
      start += regionOffset;
      end += regionOffset;

      String issueId = matcher.group(ISSUE_ID_GROUP).trim();

      IRegion region = new Region(start, end - start);
      return new TaskHyperlink(region, repository, issueId);
    }

    @Override
    public ITaskRepositoryPage getSettingsPage(TaskRepository taskRepository) {
        return new GoogleCodeRepositorySettingsPage(taskRepository);
    }

    @Override
    public boolean hasSearchPage() {
        return true;
    }

    @Override
    public String getReplyText(TaskRepository taskRepository, ITask task, ITaskComment taskComment,
            boolean includeTask) {
        if (taskComment == null) {
            return "(In reply to comment #0)";
        } else {
            return MessageFormat.format("(In reply to comment #{0})", taskComment.getNumber());
        }
    }

    @Override
    public String getAccountCreationUrl(TaskRepository taskRepository) {
        return "https://www.google.com/accounts/NewAccount";
    }

    @Override
    public String getAccountManagementUrl(TaskRepository taskRepository) {
        return "https://www.google.com/accounts/ManageAccount";
    }

}
